import req from 'request-promise'
import { equals } from 'ramda'
import { sign, sortRaw } from './util'
// import formStream from 'formstream'
// import path from 'path'
import fs from 'fs'
import * as _ from 'lodash'
const base = 'https://api.weixin.qq.com/cgi-bin/'
const api = {
  accessToken: base + 'token?grant_type=client_credential',
  ticket: base + 'ticket/getticket',
  temporary: {
    addMeterial: base + 'media/upload',
    fetchMeterial: base + 'media/get'
  },
  permanent: {
    addNewsPic: base + 'media/uploadimg',
    addNews: base + 'material/add_news',
    addMeterial: base + 'material/add_material',
    fetchMeterial: base + 'material/get_material',
    delMeterial: base + 'material/del_material',
    updateMeterial: base + 'material/update_news',
    fetchCount: base + 'material/get_materialcount',
    fetchList: base + 'material/batchget_material'
  },
  tag: {
    createTag: base + 'tags/create',
    getTags: base + 'tags/get',
    updateTag: base + 'tags/update',
    deleteTag: base + 'tags/delete',
    fansList: base + 'user/tag/get',
    batchTag: base + 'tags/members/batchtagging',
    batchCutTag: base + 'tags/members/batchuntagging',
    getIdList: base + 'tags/getidlist'
  },
  user: {
    markUser: base + 'user/info/updateremark',
    userInfo: base + 'user/info',
    batchInfo: base + 'user/info/batchget',
    userList: base + 'user/get',
    getBlackList: base + 'tags/members/getblacklist',
    addBlack: base + 'tags/members/batchblacklist',
    batchBlack: base + 'tags/members/batchunblacklist'
  },
  menu: {
    createMenu: base + 'menu/create',
    getMenu: base + 'menu/get',
    delMenu: base + 'menu/delete',
    createCustomMenu: base + 'menu/addconditional',
    delCustomMenu: base + 'menu/delconditional',
    testCustomMenu: base + 'menu/trymatch',
    getCustomConfig: base + 'get_current_selfmenu_info'
  }
}
// const stat = filepath => new Promise((resolve, reject) => {
//   fs.stat(filepath, (err, filestat) => {
//     if (err) reject(new Error(err))
//     else resolve(filestat)
//   })
// })
export default class Wechat {
  constructor(opts) {
    this.opts = Object.assign({}, opts)
    this.appID = opts.appID
    this.appSecret = opts.appSecret
    this.getAccessToken = opts.getAccessToken // 这是一个直接从微信端获取AccessToken的函数
    this.saveAccessToken = opts.saveAccessToken
    this.getTicket = opts.getTicket
    this.saveTicket = opts.saveTicket
    this.params = sortRaw

    this.fetchAccessToken()
  }
  async request(o) {
    o.method = o.method || 'GET'
    let _o = Object.assign({}, o, {json: true})
    if (o.data) {
      let url = o.url + (o.url.indexOf('?') < 0 ? '?' : '&') + this.params(o.data)
      if (o.data._payload) {
        for (let v of o.data._payload) {
          url += v
        }
      }
      _o['url'] = url
      delete _o.data
    }
    try {
      let response = await req(_o)
      return response
    } catch (error) {
      console[!console.error ? 'log' : 'error'](error)
    }
  }
  //  这是从服务端获取AccessToken的函数
  async fetchAccessToken() {
    let data = await this.getAccessToken()
    if (!this.isValid(data, 'token')) {
      data = await this.updateAccessToken()
    }
    await this.saveAccessToken(data)
    return data
  }
  async fetchTicket() {
    let data = await this.getTicket()
    if (!this.isValid(data, 'ticket')) {
      data = await this.updateTicket()
    }
    await this.saveTicket(data)
    return data
  }
  isValid(data, field) {
    if (!data || !data.expires_in || !data[field]) {
      return false
    }
    return (Date.now() < data.expires_in)
  }
  async updateTicket() {
    let { access_token } = await this.fetchAccessToken()
    const wxTicket = await this.request({
      url: api.ticket,
      data: {
        access_token: access_token,
        type: 'jsapi'
      }
    })
    wxTicket.expires_in = (wxTicket.expires_in - 20) * 1000 + Date.now()
    return {
      ticket: wxTicket.ticket,
      expires_in: wxTicket.expires_in
    }
  }
  async updateAccessToken() {
    let wxToken = await this.request({
      url: api.accessToken,
      data: {
        appid: this.appID,
        secret: this.appSecret
      }
    })
    wxToken.expires_in = (wxToken.expires_in - 20) * 1000 + Date.now()
    return {
      access_token: wxToken.access_token,
      expires_in: wxToken.expires_in
    }
  }
  async handler(ev, ...arg) {
    const tokenData = await this.fetchAccessToken()
    const opts = this[ev](tokenData.access_token, ...arg)
    const data = await this.request(opts)
    return data
  }
  uploadMeterial(token, type, meterial, permanent) {
    let form = {}
    let isMeterial = true
    let opts = {
      method: 'POST',
      data: {}
    }
    let url = api.temporary.addMeterial
    if (permanent) {
      url = api.permanent.addMeterial
      if (typeof permanent === 'object') form = permanent
      if (equals(type)('video')) {
        if (form.description) {
          form.description = JSON.stringify(form.description)
        }
      }
    }
    if (equals(type)('pic')) {
      url = api.permanent.addNewsPic
      isMeterial = false
      form.media = fs.createReadStream(meterial)
    } else if (equals(type)('news')) {
      url = api.permanent.addNews
      _.extend(form, meterial)
      isMeterial = false
    } else {
      // form = formStream()
      // const filePoint = await stat(meterial)
      // form.file('media', meterial, path.basename(meterial), filePoint.size)
      form.type = type
      form.media = fs.createReadStream(meterial)
    }
    // url = url + '?access_token=' + token
    opts.data.access_token = token
    if (isMeterial) {
      // url = url + '&type=' + type
      opts.data.type = type
    } else {
      if (type !== 'news') form.access_token = token
      // form.field('access_token', token)
    }
    opts['url'] = url
    if (type === 'news') {
      opts['body'] = form
    } else {
      opts['formData'] = form
    }
    return opts
  }
  fetchMeterial(token, mediaId, type, permanent) {
    let url = api.temporary.fetchMeterial
    let form = {
      access_token: token,
      media_id: mediaId
    }
    const opts = {
      method: 'POST'
    }
    if (permanent) {
      url = api.permanent.fetchMeterial
      opts.data = opts.body = form
    } else {
      if (type === 'video') url = url.replace('https://', 'http://')
    }
    opts['url'] = url
    return opts
  }
  delMeterial(token, mediaId) {
    const opts = {
      url: api.permanent.delMeterial,
      method: 'POST',
      data: {
        access_token: token
      },
      body: {}
    }
    opts.body = opts.data
    opts.body.media_id = mediaId
    return opts
  }
  updateMeterial(token, mediaId, news) {
    const opts = {
      url: api.permanent.updateMeterial,
      method: 'POST',
      data: {
        access_token: token
      },
      body: {
        media_id: mediaId
      }
    }
    _.extend(opts.body, news)
    return opts
  }
  fetchCount(token) {
    return {
      method: 'GET',
      url: api.permanent.fetchCount,
      data: {
        access_token: token
      }
    }
  }
  fetchList(token, type, offset, count) {
    return {
      method: 'POST',
      url: api.permanent.fetchList,
      data: {
        access_token: token
      },
      body: {
        type: type || 'image',
        offset: offset || 0,
        count: count || 10
      }
    }
  }
  createTag(token, name) {
    const opts = {
      method: 'POST',
      url: api.tag.createTag,
      data: {
        access_token: token
      }
    }
    opts.body = opts.data
    opts.body.tag = {
      name: name
    }
    return opts
  }
  getTags(token) {
    return {
      method: 'GET',
      url: api.tag.getTags,
      data: {
        access_token: token
      }
    }
  }
  updateTag(token, id, name) {
    return {
      method: 'POST',
      url: api.tag.updateTag,
      data: {
        access_token: token
      },
      body: {
        tag: {
          id: id,
          name: name
        }
      }
    }
  }
  deleteTag(token, id) {
    return {
      method: 'POST',
      url: api.tag.deleteTag,
      data: {
        access_token: token
      },
      body: {
        tag: {
          id: id
        }
      }
    }
  }
  fansList(token, tagid, nextOpenid) {
    return {
      method: 'POST',
      url: api.tag.fansList,
      data: {
        access_token: token
      },
      body: {
        tagid: tagid,
        next_openid: nextOpenid || ''
      }
    }
  }
  batchTag(token, openidList, tagid, isCut) {
    return {
      method: 'POST',
      url: !isCut ? api.tag.batchTag : api.tag.batchCutTag,
      data: {
        access_token: token
      },
      body: {
        openid_list: openidList,
        tagid: tagid
      }
    }
  }
  getIdList(token, openid) {
    return {
      method: 'POST',
      url: api.tag.getIdList,
      data: {
        access_token: token
      },
      body: {
        openid: openid
      }
    }
  }
  markUser(token, openid, remark) {
    return {
      method: 'POST',
      url: api.user.markUser,
      data: {
        access_token: token
      },
      body: {
        access_token: token,
        openid: openid,
        remark: remark
      }
    }
  }
  userInfo(token, openid, lang) {
    const opts = {
      url: api.user.userInfo,
      data: {
        access_token: token,
        openid: openid,
        lang: lang || 'zh_CN'
      }
    }
    opts.body = opts.data
    return opts
  }
  batchInfo(token, userList) {
    return {
      method: 'POST',
      url: api.user.batchInfo,
      data: {
        access_token: token
      },
      body: {
        user_list: userList
      }
    }
  }
  userList(token, nextOpenid) {
    const opts = {
      url: api.user.userList,
      data: {
        access_token: token,
        next_openid: nextOpenid || ''
      }
    }
    opts.body = opts.data
    return opts
  }
  getBlackList(token, beginOpenid) {
    return {
      method: 'POST',
      url: api.user.getBlackList,
      data: {
        access_token: token
      },
      body: {
        begin_openid: beginOpenid || ''
      }
    }
  }
  batchBlack(token, openidList, isCut) {
    return {
      method: 'POST',
      url: !isCut ? api.user.addBlack : api.user.cutBlack,
      data: {
        access_token: token
      },
      body: {
        access_token: token,
        openid_list: openidList
      }
    }
  }
  createMenu(token, button) {
    return {
      method: 'POST',
      url: api.menu.createMenu,
      data: {
        access_token: token
      },
      body: {
        button: button
      }
    }
  }
  getMenu(token) {
    return {
      url: api.menu.getMenu,
      data: {
        access_token: token
      }
    }
  }
  delMenu(token) {
    return {
      url: api.menu.delMenu,
      data: {
        access_token: token
      }
    }
  }
  createCustomMenu(token, button, rules) {
    return {
      method: 'POST',
      url: api.menu.createCustomMenu,
      data: {
        access_token: token
      },
      body: {
        button: button,
        matchrule: rules
      }
    }
  }
  delCustomMenu(token, menuid) {
    return {
      method: 'POST',
      url: api.menu.delCustomMenu,
      data: {
        access_token: token
      },
      body: {
        menuid: menuid
      }
    }
  }
  testCustomMenu(token, userId) {
    return {
      method: 'POST',
      url: api.menu.testCustomMenu,
      data: {
        access_token: token
      },
      body: {
        user_id: userId
      }
    }
  }
  getCustomConfig(token) {
    return {
      url: api.menu.getCustomConfig,
      data: {
        access_token: token
      }
    }
  }
  sign(ticket, url) {
    return sign(ticket, url)
  }
}
